package com.oracle.coherence.patterns.security;

import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.kerberos.KerberosPrincipal;
import javax.security.auth.login.LoginException;
import java.util.Map;
import java.util.Set;

/**
 * This is an extension of the {@link com.sun.security.auth.module.Krb5LoginModule} class.
 * This class requires an extra parameter to be set either using the
 * <code>serviceprincipalname</code> property in the JAAS configuration file
 * seetings for this login module or by setting the <code>sun.security.krb5.spn</code>
 * system property. If using plain Kerberos the SPN is set to the identity that is
 * running the server process to connect to. If using Active Directory this value
 * can be set to either the identity running the server process or to the value
 * of an SPN attached to that identity.
 * <p/>
 *
 * @author Jonathan Knight
 */
public class KrbLoginModule extends com.sun.security.auth.module.Krb5LoginModule {

    /**
     * The Subject that will have the KrbTokenPrincipal added to it
     */
    private Subject subject;
    /**
     * The service principal name of the server process to authenticate to
     */
    private String spn;

    /**
     * {@inheritDoc}
     */
    @Override
    public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) {
        super.initialize(subject, callbackHandler, sharedState, options);
        this.subject = subject;

        boolean debug = "true".equalsIgnoreCase((String) options.get("debug"));
        spn = (String) options.get("serviceprincipalname");
        spn = System.getProperty("security.krb.servicename", spn);
        if (debug) {
            System.out.println("serviceprincipalname is " + spn);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean commit() throws LoginException {
        boolean success = false;

        if (super.commit()) {
            Set<KerberosPrincipal> principals = subject.getPrincipals(KerberosPrincipal.class);
            if (principals.size() > 0) {
                KerberosPrincipal kp = principals.iterator().next();

                if (spn == null || spn.length() == 0) {
                    throw new LoginException("Configuration Error - " +
                            "No serviceprincipalname in JAAS configuration " +
                            "or sun.security.krb5.spn property");
                }

                KrbTicketPrincipal ticketPrincipal = new KrbTicketPrincipal(kp.getName(), spn);
                ticketPrincipal.setAuthenticated(true);
                ticketPrincipal.setLocal(true);
                //subject.getPrincipals().clear();
                subject.getPrincipals().add(ticketPrincipal);
                success = true;
            } else {
                throw new LoginException("Cannot find KerberosPrincipal in Subject");
            }
        }

        return success;
    }
}
